#lang racket

(define ObjektArt_Figur 0)
(define ObjektArt_Waffe 1)
(define ObjektArt_Zimmer 2)
(define ObjektArt_COUNT 3)


(define Oberst_Von_GatowStr "Oberst von Gatow")
(define Professor_BloomStr "Professor Bloom")
(define Reverend_GruenStr "Reverend Gruen")
(define Baronin_Von_PorzStr "Baronin von Porz")
(define Fraeulein_GloriaStr "Fraulein Gloria")
(define Frau_WeissStr "Frau Weiss")

(define Oberst_Von_Gatow 0)
(define Professor_Bloom 1)
(define Reverend_Gruen 2)
(define Baronin_Von_Porz 3)
(define Fraeulein_Gloria 4)
(define Frau_Weiss 5)
(define FIGUREN_COUNT 6)

(define FigurToString
	(lambda (x)
		(cond 
			((= x Oberst_Von_Gatow) Oberst_Von_GatowStr)
			((= x Professor_Bloom) Professor_BloomStr)
			((= x Reverend_Gruen) Reverend_GruenStr)
			((= x Baronin_Von_Porz) Baronin_Von_PorzStr)
			((= x Fraeulein_Gloria) Fraeulein_GloriaStr)
			((= x Frau_Weiss) Frau_WeissStr)
			(else (error "Figur gibt es nicht"))
		)
	)
)


(define PistoleStr "Pistole")
(define DolchStr "Dolch")
(define SeilStr "Seil")
(define KerzenleuchterStr "Kerzenleuchter")
(define RohrzangeStr "Rohrzange")
(define HeizungsrohrStr "Heizungsrohr")

(define Pistole 0)
(define Dolch 1)
(define Seil 2)
(define Kerzenleuchter 3)
(define Rohrzange 4)
(define Heizungsrohr 5)
(define WAFFEN_COUNT 6)

(define WaffeToString
	(lambda (x)
		(cond 
			((= x Pistole) PistoleStr)
			((= x Dolch) DolchStr)
			((= x Seil) SeilStr)
			((= x Kerzenleuchter) KerzenleuchterStr)
			((= x Rohrzange) RohrzangeStr)
			((= x Heizungsrohr) HeizungsrohrStr)
			(else (error "Waffe gibt es nicht"))
		)
	)
)


(define KuecheStr "Kueche")
(define MusikzimmerStr "Musikzimmer")
(define WintergartenStr "Wintergarten")
(define BillardzimmerStr "Billardzimmer")
(define BibliothekStr "Bibliothek")
(define ArbeitszimmerStr "Arbeitszimmer")
(define EingangshalleStr "Eingangshalle")
(define SalonStr "Salon")
(define SpeisezimmerStr "Speisezimmer")

(define Kueche 0)
(define Musikzimmer 1)
(define Wintergarten 2)
(define Billardzimmer 3)
(define Bibliothek 4)
(define Arbeitszimmer 5) 
(define Eingangshalle 6)
(define Salon 7)
(define Speisezimmer 8)
(define ZIMMER_COUNT 9)

(define ZimmerToString
	(lambda (x)
		(cond 
			((= x Kueche) KuecheStr)
			((= x Musikzimmer) MusikzimmerStr)
			((= x Wintergarten) WintergartenStr)
			((= x Billardzimmer) BillardzimmerStr)
			((= x Bibliothek) BibliothekStr)
			((= x Arbeitszimmer) ArbeitszimmerStr)
			((= x Eingangshalle) EingangshalleStr)
			((= x Salon) SalonStr)
			((= x Speisezimmer) SpeisezimmerStr)
			(else (error "Zimmer gibt es nicht"))
		)
	)
)



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Fremder Code, um Read Buffer zu flushen
(define flush-input
	(let ([buf (make-bytes 10)])
	(lambda ()
		(let loop ()
			(let ([n (read-bytes-avail!* buf)])
				(unless 
					(or 
						(eof-object? n) 
						(zero? (read-bytes-avail!* buf))
					)
					(loop)
				)
			)
		)
	)))
(define (flush-and- read) (flush-input) (read))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; erzeugt eine Liste mit allen Karten und setzt diese auf 1
; TODO schreiben
(define initializeBekannteKarten
	(lambda ()
		(vector
			(vector ;ObjektArt_Figur 
				(vector Oberst_Von_Gatow 1.0)
				(vector Professor_Bloom 1.0)
				(vector Reverend_Gruen 1.0)
				(vector Baronin_Von_Porz 1.0)
				(vector Fraeulein_Gloria 1.0)
				(vector Frau_Weiss 1.0)
			)
			(vector ;ObjektArt_Waffe
				(vector Pistole 1.0)
				(vector Dolch 1.0)
				(vector Seil 1.0)
				(vector Kerzenleuchter 1.0)
				(vector Rohrzange 1.0)
				(vector Heizungsrohr 1.0)
			)
			(vector ;ObjektArt_Waffe
				(vector Pistole 1.0)
				(vector Dolch 1.0)
				(vector Seil 1.0)
				(vector Kerzenleuchter 1.0)
				(vector Rohrzange 1.0)
				(vector Heizungsrohr 1.0)
			)
			(vector ;ObjektArt_Zimmer
				(vector Kueche 1.0)
				(vector Musikzimmer 1.0)
				(vector Wintergarten 1.0)
				(vector Billardzimmer 1.0)
				(vector Bibliothek 1.0)
				(vector Arbeitszimmer 1.0)
				(vector Eingangshalle 1.0)
				(vector Salon 1.0)
				(vector Speisezimmer 1.0)
			)
		)
	)
)


; TODO: bekannteKarten deklarieren und initialisieren
; d.h. alles auf 1 setzen
(define bekannteKarten (initializeBekannteKarten)) ; eventuell Global machen(done)
; GLOBALE VARIABLE

(define checkNumber
	(lambda (number low high)
		(if (not number)
			'() ; true
			(if (and (>= number low) (<= number high))  ; false -> ist nummer
				number ; true
				'() ; false
			)
		)
	)
)

(define deleteItemFromList
	(lambda (it lst)
		(cond
			((null? lst) lst)
			((or (equal? it (car lst)) (list? (car lst))(null? (car lst))) (deleteItemFromList it (cdr lst)) )
			(else (cons (car lst) (deleteItemFromList it (cdr lst))) )
		)
	)
)

(define extractNumbersFromString
	(lambda (str low high)
		; Splittet den String nach Leerzeichen in Liste, macht per Map aus der StringList eine NumberList
		(define numberList (map (lambda (x) (checkNumber x low high)) (map string->number (string-split str))))
		(set! numberList (deleteItemFromList '() numberList))
		; Returnt diese Nummernliste
		numberList
	)
)


(define printListeImpl
	(lambda (x count StrFunc)
		(write (+ x 0))
		(display ".- ")
		(write (StrFunc x))
		(newline)
		
		(if (= (+ x 1) count)
			'() ; true
			(printListeImpl (+ x 1) count StrFunc) ; false
		)
	)
)

(define printZimmer
	(lambda ()
		(newline)
		(printListeImpl 0 ZIMMER_COUNT ZimmerToString)
	)
)

(define printWaffen
	(lambda ()
		(newline)
		(printListeImpl 0 WAFFEN_COUNT WaffeToString)
	)
)

(define printFiguren
	(lambda ()
		(newline)
		(printListeImpl 0 FIGUREN_COUNT FigurToString)
	)
)


; liest alle Karten ein der Spieler ausgeteilt bekommt am Anfang vom Spiel
(define leseStarthandkartenEin
	(lambda ()
		(flush-input)
		(display "\nWelche Karten hast du?\n")
		(display "Liste mit Zimmern:")
		(printZimmer)
		(define ZimmerEingabeListe (extractNumbersFromString (read-line) 0 ZIMMER_COUNT))
		
		(display "\nListe mit Waffen:")
		(printWaffen)
		(define WaffenEingabeListe (extractNumbersFromString (read-line) 0 WAFFEN_COUNT))
		
		(display "\nListe mit Figuren:")
		(printFiguren)
		(define FigurenEingabeListe (extractNumbersFromString (read-line) 0 FIGUREN_COUNT))
		
		;(define v (vector ObjektArt_COUNT FigurenEingabeListe WaffenEingabeListe ZimmerEingabeListe))
		(define v (list FigurenEingabeListe WaffenEingabeListe ZimmerEingabeListe))
		
		v ; return
	)
)


(define setToZero
	(lambda (vek lst counter)
		(vector-set! (vector-ref vek (list-ref lst counter)) 1 0.0)
		(if (< (+ 1 counter) (length lst))
			(setToZero vek lst (+ 1 counter)) ; true
			'(); false
		)
	)
)

; setzt alle Karten die der Spieler hat als bekannt auf 0
(define setzteBekanntekartenAufNull
	(lambda (bekannteKarten neueKarten)
		(setToZero (vector-ref bekannteKarten ObjektArt_Figur) (list-ref neueKarten ObjektArt_Figur) 0)
		(setToZero (vector-ref bekannteKarten ObjektArt_Waffe) (list-ref neueKarten ObjektArt_Waffe) 0)
		(setToZero (vector-ref bekannteKarten ObjektArt_Zimmer) (list-ref neueKarten ObjektArt_Zimmer) 0)
	)
)

(define halfTheValues
	(lambda (vec lst counter func)
		(define tmp (vector-ref (vector-ref (vector-ref vec counter) (vector-ref lst counter)) 1))
		(vector-set! (vector-ref (vector-ref vec counter) (vector-ref lst counter)) 1 (func tmp)) 
		(if (= (+ 1 counter) (vector-length lst))
			'(); true
			(halfTheValues vec lst (+ 1 counter) func) ; false
		)
	)
)

(define verarbeiteSpielerEingabe
	(lambda (bekannteKarten gegenspielerEingabe)
		(halfTheValues bekannteKarten gegenspielerEingabe 0 (lambda (x) (* x 0.5)))
	)
)

(define leseAuswahlAnKombinationEin 
	(lambda (bekannteKarten)
		(display "\nFiguren: ")
		(printFiguren)
		(display "Figur: ")
		(define figur (read))
		
		(display "\nWaffen: ")
		(printWaffen)
		(display "Waffe: ")
		(define waffe (read))
		
		(display "\nZimmer:")
		(printZimmer)
		(display "Zimmer: ")
		(define zimmer (read))
		
		(vector figur waffe zimmer)
	)
)

(define readGezeigteKarte
	(lambda (spielerGezogen)
		(display "0.- ") (write (FigurToString (vector-ref spielerGezogen ObjektArt_Figur)))
		(display "\n1.- ") (write (WaffeToString (vector-ref spielerGezogen ObjektArt_Waffe)))
		(display "\n2.- ") (write (ZimmerToString (vector-ref spielerGezogen ObjektArt_Zimmer)))
		
		(display "\nWelche Karte hat er gezeigt: ")
		(read)
	)
)

(define max_elementImpl
	(lambda (vec greatest counter cmp)
		(if (not (< counter (vector-length vec)))
			greatest ; true
			(let ( ; false
					(next (cmp greatest (vector-ref vec counter)))
					(c (+ 1 counter))
				)
				(max_elementImpl vec next c cmp )
			)
 		)
	)
)

(define max_element
	(lambda (vec counter cmp)
		(max_elementImpl vec (vector-ref vec 0) counter cmp)
	)
)

(define PairGreater
	(lambda (vec1 vec2)
		(if (> (vector-ref vec1 1) (vector-ref vec2 1))
			vec1
			vec2
		)
	)
)

(define empfehlungImpl
	(lambda (bekannteKarten)
		(define fig (max_element (vector-ref bekannteKarten ObjektArt_Figur) 0 PairGreater))
		(define waf (max_element (vector-ref bekannteKarten ObjektArt_Waffe) 0 PairGreater))
		(define zim (max_element (vector-ref bekannteKarten ObjektArt_Zimmer) 0 PairGreater))
		
		(vector fig waf zim)
	)
)

(define empfehlung
	(lambda (bekannteKarten)
		(empfehlungImpl bekannteKarten)
	)
)

; der Spielzug des Spielers
(define Spielerzug
	(lambda (Anzahl)
		(flush-input)
		(newline) (newline)
		
		; Empfehlung aussprechen
		(define empfohlen (empfehlung bekannteKarten))
		(display "Ich empfehle folgende Karten zu ziehen:\n")
		(write (FigurToString (vector-ref (vector-ref empfohlen ObjektArt_Figur) 0))) (display " ")
		(write (WaffeToString (vector-ref (vector-ref empfohlen ObjektArt_Waffe) 0))) (display " ")
		(write (ZimmerToString (vector-ref (vector-ref empfohlen ObjektArt_Zimmer) 0))) (newline)
		
		(display "\nWelche Karten hast du verlangt: \n")
		(define spielerGezogen (leseAuswahlAnKombinationEin bekannteKarten))
		
		(display "Welcher Spieler hat geantwortet (Abstand im Uhrzeigersinn, 0 falls keiner: ")
		(define antwortSpieler (read))
		
		(if (< antwortSpieler Anzahl)
			'()
			(error "falsche Spielerantwort Eingabe")
		)
		
		(if (= 0 antwortSpieler)
			'() ; true
			(let ( ; false
					(art (readGezeigteKarte spielerGezogen))
				)
				(vector-set! (vector-ref (vector-ref bekannteKarten art) (vector-ref spielerGezogen art)) 1 0.0)
			)
		)
		(newline)
	)
)

; ein Spielzug der Gegenspieler
(define Gegenspielerzug
	(lambda (Zaehler Anzahl)
		(display "\nWas hat Spieler ")
		(write (+ Zaehler 1))
		(display " verlangt: ") (newline)
	
		; ToDo Eingabe
		(define gegenspielerEingabe (leseAuswahlAnKombinationEin bekannteKarten))
		
		(display "Welcher Spieler hat geantwortet (Abstand im Uhrzeigersinn, 0 falls keiner): ")
		(define antwortSpieler (read))
		(if (< antwortSpieler Anzahl)
			'()
			(error "falsche Spielerantwort Eingabe")
		)
		
		(if (= antwortSpieler 0)
			'()
			(verarbeiteSpielerEingabe bekannteKarten gegenspielerEingabe) ; verarbeiteSpielerEingabe ToDo
		)
	)
)


; eigentliche Hauptschleife pro Spielrunde
; es wird über alle Spieler iteriert und dementsprechend für den Spieler
; oder die Gegenspieler anders reagiert (Funktiosnaufrufe)
(define Spielerschleife
	(lambda (zaehler Anzahl besonderePosition)
		(display "\nHat Spieler ")
		(write (+ zaehler 1))
		(display " nur gewuerfelt (j/n): ")
		(flush-input)
		(define nurGewuerfelt (read-line))
		
		(if (string-ci=? nurGewuerfelt "j")
			'() ; true
			(if (= zaehler besonderePosition) ; false
				(Spielerzug Anzahl); true
				(Gegenspielerzug zaehler Anzahl) ; false
			)
		)
		
		; Iteration
		(if (< (+ 1 zaehler) Anzahl)
			(Spielerschleife (+ zaehler 1) Anzahl besonderePosition) ; true
			'() ; false
		)
	)
)

; Hauptschleife pro Spielrunde
; kapselt wegen der Parameter jedoch die eigentliche Schleifenfunktion
(define SpielerSchleife
	(lambda (spielerAnzahl spielerPlatz)
		(Spielerschleife 0 spielerAnzahl spielerPlatz)
	)
)

; Mainloop, läuft solange das Spiel nicht zu Ende ist
; bildet jeweils eine Spielrunde ab
; besteht aus der Schleife, die alle Spieler abfragt
; und der Frage ob Spiel gelöst ist
(define mainLoop
	(lambda (spielerAnzahl spielerPlatz)
		; eigenliche Schleifenarbeit
		
		(display "X**********************************************X") (newline)
		(display "X************ Spielrunde beginnt **************X") (newline)
		
		(SpielerSchleife spielerAnzahl spielerPlatz)
		
		; Schleifenwiederholung/Abbruch
		(newline)
		(display "Ist das Spiel vorbei(j/n) ? ")
		(flush-input) ; Buffer Clear, weil EOF noch im Lese Puffer steht
		(define isGameOver (read-char))
		(if (char? isGameOver)
			(if (char=? isGameOver #\j); true
				(set! isGameOver #t) ; true
				(set! isGameOver #f) ; false
			)
			(set! isGameOver #f); false
		)
		
		(display "X************* Spielrunde endet ***************X") (newline)
		(display "X**********************************************X") (newline)
		
		(if (not (not isGameOver))
			'(); true
			(mainLoop spielerAnzahl spielerPlatz); false
		)
	)
)

; Hauptfunktion
; liest spielerDaten ein
(define main
	(lambda ()
		(define spielerAnzahl 0)
		(define spielerPlatz 0)
		
		; Liest Spieleranzahl ein und prüft dies
		(display "Gib die Anzahl der Spieler ein: ")
		(set! spielerAnzahl (read))
		(if (or (> spielerAnzahl 6) (< spielerAnzahl 2) )
			(error "Falsche Spieleranzahl"); true
			'(); false
		)
		
		; Liest Sitzplatz des Spielers ein und prüft dies
		(display "Gib deinen Platz ein: ")
		(set! spielerPlatz (- (read) 1) )
		(if (or (>= spielerPlatz spielerAnzahl) (< spielerPlatz 0) )
			(error "Falscher Spielerplatz"); true
			'(); false
		)
		
		; TODO: Handkarten einlesen und verarbeiten
		; d.h. alle eigenen Kartenwerte einlesen
		(define handkarten (leseStarthandkartenEin))
		
		; TODO: Funktion schreiben um auf 0 zu setzen
		(setzteBekanntekartenAufNull bekannteKarten handkarten)
		
		(display "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") (newline)
		(display "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") (newline)
		(display "X**********************************************X") (newline)
		(display "X*************** Spielbegin *******************X") (newline)
		(display "X**********************************************X") (newline)
		
		; Hauptschleife
		(mainLoop spielerAnzahl spielerPlatz)
		
		(display "X**********************************************X") (newline)
		(display "X*************** Spielende ********************X") (newline)
		(display "X**********************************************X") (newline)
	)
)

(main)


